package com.func.infrastructure.persistent.repository;

import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy;
import com.alibaba.fastjson.JSON;
import com.func.domain.award.model.valobj.AccountStatusVO;
import com.func.domain.credit.model.aggregate.TradeAggregate;
import com.func.domain.credit.model.entity.CreditAccountEntity;
import com.func.domain.credit.model.entity.CreditOrderEntity;
import com.func.domain.credit.model.entity.TaskEntity;
import com.func.domain.credit.repository.ICreditRepository;
import com.func.infrastructure.event.EventPublisher;
import com.func.infrastructure.persistent.dao.credit.IUserCreditAccountDao;
import com.func.infrastructure.persistent.dao.credit.IUserCreditOrderDao;
import com.func.infrastructure.persistent.dao.task.ITaskDao;
import com.func.infrastructure.persistent.po.credit.UserCreditAccount;
import com.func.infrastructure.persistent.po.credit.UserCreditOrder;
import com.func.infrastructure.persistent.po.task.Task;
import com.func.infrastructure.persistent.redis.IRedisService;
import com.func.types.common.Constants;
import com.func.types.enums.ResponseCode;
import com.func.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * CreditRepository
 * </p>
 *
 * @author Simon
 * @version 2025.03.23.1.0.0
 * @description
 * @since 2025-03-23
 */

@Slf4j
@Repository
public class CreditRepository implements ICreditRepository {
    @Resource
    private IRedisService redisService;
    @Resource
    private IDBRouterStrategy dbRouter;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private EventPublisher eventPublisher;

    @Resource
    private IUserCreditAccountDao userCreditAccountDao;
    @Resource
    private IUserCreditOrderDao userCreditOrderDao;
    @Resource
    private ITaskDao taskDao;
    @Override
    public void saveUserCreditTradeOrder(TradeAggregate tradeAggregate) {
        String userId = tradeAggregate.getUserId();
        CreditAccountEntity creditAccountEntity = tradeAggregate.getCreditAccountEntity();
        CreditOrderEntity creditOrderEntity = tradeAggregate.getCreditOrderEntity();
        TaskEntity taskEntity = tradeAggregate.getTaskEntity();

        // 积分账户
        UserCreditAccount userCreditAccount = new UserCreditAccount();
        userCreditAccount.setUserId(creditAccountEntity.getUserId());
        userCreditAccount.setTotalAmount(creditAccountEntity.getAdjustAmount());
        userCreditAccount.setAvailableAmount(creditAccountEntity.getAdjustAmount());
        userCreditAccount.setAccountStatus(AccountStatusVO.open.getCode());

        // 积分订单
        UserCreditOrder userCreditOrder = new UserCreditOrder();
        userCreditOrder.setUserId(creditOrderEntity.getUserId());
        userCreditOrder.setOrderId(creditOrderEntity.getOrderId());
        userCreditOrder.setTradeName(creditOrderEntity.getTradeName().getName());
        userCreditOrder.setTradeType(creditOrderEntity.getTradeType().getCode());
        userCreditOrder.setTradeAmount(creditOrderEntity.getTradeAmount());
        userCreditOrder.setOutBusinessNo(creditOrderEntity.getOutBusinessNo());

        // 任务
        Task task = new Task();
        task.setUserId(taskEntity.getUserId());
        task.setTopic(taskEntity.getTopic());
        task.setMessageId(taskEntity.getMessageId());
        task.setMessage(JSON.toJSONString(taskEntity.getMessage()));
        task.setState(taskEntity.getState().getCode());

        // 入库
        RLock lock = redisService.getLock(Constants.RedisKey.USER_CREDIT_ACCOUNT_LOCK + userId + Constants.UNDERLINE + creditOrderEntity.getOutBusinessNo());
        try{
            lock.lock(3, TimeUnit.SECONDS);
            dbRouter.doRouter(userId);
            transactionTemplate.execute(status -> {
                try {
                    // 1. 保存账户积分
                    UserCreditAccount userCreditAccountRes = userCreditAccountDao.queryUserCreditAccount(userCreditAccount);
                    if (userCreditAccountRes == null) {
                        userCreditAccountDao.insert(userCreditAccount);
                    } else {
                        BigDecimal availableAmount = userCreditAccount.getAvailableAmount();
                        if (availableAmount.compareTo(BigDecimal.ZERO) >= 0) {
                            userCreditAccountDao.updateAddAmount(userCreditAccount);
                        } else {
                            int subtractionCount = userCreditAccountDao.updateSubtractionAmount(userCreditAccount);
                            if (1 != subtractionCount) {
                                status.setRollbackOnly();
                                throw new AppException(ResponseCode.USER_CREDIT_ACCOUNT_NO_AVAILABLE_AMOUNT.getCode(), ResponseCode.USER_CREDIT_ACCOUNT_NO_AVAILABLE_AMOUNT.getInfo());
                            }
                        }
                    }
                    // 2. 保存账户订单
                    userCreditOrderDao.insert(userCreditOrder);
                    taskDao.insert(task);
                } catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.error("调整账户积分额度异常, 唯一索引冲突 userId: {} orderId: {}", userId, userCreditOrder.getOrderId(), e);
                } catch (Exception e) {
                    status.setRollbackOnly();
                    log.error("调整账户积分额度失败 userId: {} orderId: {}", userId, creditOrderEntity.getOrderId(), e);
                }
                return 1;
            });
        } finally {
            dbRouter.clear();
            lock.unlock();
        }

        try {
            // 发送消息 [在事务外执行, 如果失败还有失败补偿]
            eventPublisher.publish(task.getTopic(), task.getMessage());
            taskDao.updateTaskSendMessageCompleted(task);
            log.info("调整账户积分记录, 发送MQ消息完成 userId: {} orderId: {} topic: {}", userId, creditOrderEntity.getOrderId(), task.getTopic());
        } catch (Exception e){
            taskDao.updateTaskSendMessageFail(task);
            log.error("调整账户积分记录, 发送MQ消息失败 userId: {} orderId: {} topic: {}", userId, creditOrderEntity.getOrderId(), task.getTopic(), e);
        }
    }

    @Override
    public CreditAccountEntity queryUserCreditAccount(String userId) {
        UserCreditAccount userCreditAccountReq = new UserCreditAccount();
        userCreditAccountReq.setUserId(userId);
        try {
            dbRouter.doRouter(userId);
            UserCreditAccount userCreditAccount = userCreditAccountDao.queryUserCreditAccount(userCreditAccountReq);
            return CreditAccountEntity.builder().adjustAmount(userCreditAccount.getAvailableAmount()).build();
        } finally {
            dbRouter.clear();
        }
    }
}
